package cc.rengu.igas.mcps.core.service.trans.common;

import cc.rengu.igas.mcps.common.constant.McpsParamConstant;
import cc.rengu.igas.mcps.common.dao.TermKeyMapper;
import cc.rengu.igas.mcps.common.dao.impl.TermKeyMapperImpl;
import cc.rengu.igas.mcps.common.entity.TermKey;
import cc.rengu.igas.mcps.common.enums.RespCodeEnum;
import cc.rengu.igas.mcps.core.service.base.ManageService;
import cc.rengu.igas.mcps.facade.base.Header;
import cc.rengu.igas.mcps.facade.bean.TermInfo;
import cc.rengu.igas.mcps.facade.request.PosDownloadTMKRequest;
import cc.rengu.igas.mcps.facade.response.PosDownloadTMKResponse;
import cc.rengu.igas.share.core.model.MchntTermInfo;
import cc.rengu.igas.share.core.realize.MchntService;
import cc.rengu.igas.share.core.realize.impl.MchntServiceImpl;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.constant.TreeNodeConstant;
import cc.rengu.oltp.service.common.dao.CertInfoMapper;
import cc.rengu.oltp.service.common.dao.SecPlanInfoMapper;
import cc.rengu.oltp.service.common.dao.SrcChannelInfoMapper;
import cc.rengu.oltp.service.common.dao.impl.CertInfoMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.SecPlanInfoMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.SrcChannelInfoMapperImpl;
import cc.rengu.oltp.service.common.entity.CertInfo;
import cc.rengu.oltp.service.common.entity.SecPlanInfo;
import cc.rengu.oltp.service.common.entity.SrcChannelInfo;
import cc.rengu.oltp.service.common.entity.SysParam;
import cc.rengu.oltp.service.common.enums.OltpRpcdEnum;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.service.model.BizResponse;
import cc.rengu.oltp.service.realize.SysParamService;
import cc.rengu.oltp.service.realize.impl.SysParamServiceImpl;
import cc.rengu.oltp.utility.util.*;
import com.alibaba.fastjson.JSON;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;

import javax.crypto.Cipher;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.interfaces.RSAPrivateKey;
import java.util.Objects;


/**
 * 智能pos端发起终端主密钥下载
 * create by zhangbin 2020/03
 */
public class PosDownloadTMKService extends ManageService {

    @Override
    protected Object bizBeforeCust() throws Exception {
        PosDownloadTMKRequest posDownloadTMKRequest = new PosDownloadTMKRequest();
        ConvertUtil.convertOutput(posDownloadTMKRequest);
        return posDownloadTMKRequest;
    }

    @Override
    protected BizResponse bizProcessCust(Object request) throws Exception {
        PosDownloadTMKRequest posDownloadTMKRequest = (PosDownloadTMKRequest) request;
        Header header = posDownloadTMKRequest.getHeader();
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        BizResponse bizResponse = new BizResponse();
        PosDownloadTMKResponse posDownloadTMKResponse = new PosDownloadTMKResponse();
        int iRetCode;
        /*获取机构标识*/
        String instId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.INST_ID);
        /*获取商户号*/
        String mchntNo = posDownloadTMKRequest.getMchntNo();
        /*获取终端号*/
        String termNo = posDownloadTMKRequest.getTermNo();
        /* 获取终端信息 */
        MchntService mchntService = new MchntServiceImpl();
        MchntTermInfo mchntTermInfo = mchntService.getMchntTermInfo(instId, mchntNo, termNo);
        /* 更新终端经纬度信息 */
        TermInfo termInfo = posDownloadTMKRequest.getTermInfo();
        if (null != termInfo && !StringUtil.isEmptyOrNull(termInfo.getLatitude()) && !StringUtil.isEmptyOrNull(termInfo.getLongitude())) {
            mchntTermInfo.setLatitude(posDownloadTMKRequest.getTermInfo().getLatitude());
            mchntTermInfo.setLongitude(posDownloadTMKRequest.getTermInfo().getLongitude());
            mchntService.updateMchntTermInfo("A0", mchntTermInfo);
        }else {
            rglog.error("终端主密钥下载未上送经纬度信息，下载失败!");
            throw new BizException(RespCodeEnum.BIZ_PARAM_CHECK_ERROR.getRespCode(),RespCodeEnum.BIZ_PARAM_CHECK_ERROR.getRespDesc());
        }

        //通过Redis获取交易渠道信息
        String srcChannelInfoKey = header.getInstId().trim() + header.getSrcSysId().trim() + header.getChanlId().trim();
        SrcChannelInfo srcChannelInfo = JSON.parseObject(RedisUtil.hashGet(AppParamConstant.CHANNEL_ACQ_INFO_CACHE, srcChannelInfoKey), SrcChannelInfo.class);
        if (null == srcChannelInfo || StringUtil.isEmptyOrNull(srcChannelInfo.getSecPlanId())) {
            srcChannelInfoKey = header.getInstId().trim() + header.getSrcSysId().trim() + "*";
            srcChannelInfo = JSON.parseObject(RedisUtil.hashGet(AppParamConstant.CHANNEL_ACQ_INFO_CACHE, srcChannelInfoKey), SrcChannelInfo.class);
            if (null == srcChannelInfo || StringUtil.isEmptyOrNull(srcChannelInfo.getSecPlanId())) {
                SrcChannelInfoMapper srcChannelInfoMapper = new SrcChannelInfoMapperImpl();
                srcChannelInfo = srcChannelInfoMapper.selectSrcChannelInfoByPrimaryKey(txnInfo.getSrcTxnAuthCfg().getInstId().trim(), txnInfo.getSrcTxnAuthCfg().getMsgSrcId().trim(), txnInfo.getSrcTxnAuthCfg().getTransChannelId().trim());
                if (null == srcChannelInfo || StringUtil.isEmptyOrNull(srcChannelInfo.getSecPlanId())) {
                    srcChannelInfo = srcChannelInfoMapper.selectSrcChannelInfoByPrimaryKey(txnInfo.getSrcTxnAuthCfg().getInstId().trim(), txnInfo.getSrcTxnAuthCfg().getMsgSrcId().trim(), "*");
                }
            }
        }
        if (null == srcChannelInfo || StringUtil.isEmptyOrNull(srcChannelInfo.getSecPlanId())) {
            rglog.error("交易渠道<{}>的细分渠道<{}>未配置或已停用！", txnInfo.getSrcTxnAuthCfg().getMsgSrcId(), txnInfo.getSrcTxnAuthCfg().getTransChannelId());
            throw new BizException(OltpRpcdEnum.CHANNEL_DISABLED);
        }

        //安全标识
        String secPlanId = srcChannelInfo.getSecPlanId();
        SecPlanInfoMapper secPlanInfoMapper = new SecPlanInfoMapperImpl();
        SecPlanInfo secPlanInfo = secPlanInfoMapper.selectSecPlanInfoByPrimaryKey(header.getInstId(), secPlanId, McpsParamConstant.PRIVATE_DECRYPT_CERT_TYPE);
        if (null == secPlanInfo) {
            rglog.error("安全计划查询为空！instId:<{}>,secPlanId:<{}>,secPlanType:<{}>", header.getInstId(),
                    secPlanId, McpsParamConstant.PRIVATE_DECRYPT_CERT_TYPE);
            throw new BizException(RespCodeEnum.CERT_INFO_NOT_EXIST.getRespCode(), "机构安全计划查询为空");
        } else if (AppParamConstant.NO.equals(secPlanInfo.getSecPlanStatus())) {
            rglog.error("安全计划状态已失效！instId:<{}>,secPlanId:<{}>,secPlanType:<{}>,状态:<{}>", header.getInstId(),
                    secPlanId, McpsParamConstant.PRIVATE_DECRYPT_CERT_TYPE, secPlanInfo.getSecPlanStatus());
            throw new BizException(RespCodeEnum.CERT_INFO_NOT_EXIST.getRespCode(), "机构安全计划状态失效");
        }

        //查询证书表
        CertInfoMapper certInfoMapper = new CertInfoMapperImpl();
        String certIndex = secPlanInfo.getInstId() + secPlanInfo.getSecPlanId() + secPlanInfo.getSecPlanType();
        CertInfo certInfo = certInfoMapper.selectCertInfoByPrimaryKey(certIndex);
        if (null == certInfo) {
            rglog.error("机构私钥证书查询为空！certIndex:<{}>", certIndex);
            throw new BizException(RespCodeEnum.CERT_INFO_NOT_EXIST.getRespCode(), "机构公钥证书查询为空");
        } else if (AppParamConstant.NO.equals(certInfo.getKeyStatus())) {
            rglog.error("机构私钥证书查询证书状态失效！certIndex:<{}>", certIndex);
            throw new BizException(RespCodeEnum.CERT_INFO_NOT_EXIST.getRespCode(), "机构公钥证书状态已失效");
        }

        String privateKey;
        if ("2".equals(certInfo.getCertSaveType())) {
            /* 本地文件 */
            rglog.info("证书以文件的方式存放，certIndex:<{}>,certSavePath:<{}>", certIndex, certInfo.getCertSavePath());
            try {
                privateKey = FileUtils.readFileToString(new File(certInfo.getCertSavePath()), StandardCharsets.UTF_8);
            } catch (IOException e) {
                rglog.error("读取证书文件异常:{}", e.getMessage());
                throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
            }
        } else if ("0".equals(certInfo.getCertSaveType())) {
            /* 数据库存储 */
            privateKey = certInfo.getCertValue();
        } else {
            rglog.error("暂不支持处理除本地数据存储或本地文件外的证书");
            throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
        }

        /*初始化密钥值*/
        /*初始化密钥校验值*/
        /*解密AES对称密钥 */
        String aesKey = privateKeyDecrypt(Base64.decodeBase64(posDownloadTMKRequest.getAesKey()), RSAUtil.getPrivateKeyFromBase64String(privateKey));
        /*随机生成终端主密钥*/
        String tmkKey = SM4Util.generateSm4Key(128);
        String tmkValue = AESUtil.pkcs5EcbEncryptData(Base64.decodeBase64(aesKey), ByteUtil.hexStringToByte(tmkKey));
        SysParamService sysParamService = new SysParamServiceImpl();
        /*根据参数类型和参数键值查询参数信息*/
        SysParam sysParam = sysParamService.getSysParamInfo(instId, McpsParamConstant.MCPS_SYS_ID, McpsParamConstant.ALG_TYPE);
        if (null == sysParam) {
            rglog.error("算法查询为空！instId:<{}>,secPlanId:<{}>,secPlanType:<{}>", header.getInstId(),
                    secPlanId, McpsParamConstant.PRIVATE_DECRYPT_CERT_TYPE);
            throw new BizException(RespCodeEnum.CERT_INFO_NOT_EXIST.getRespCode(), "算法查询为空");
        }
        /*生成密钥校验值checkValue*/
        String tmkChkVal = "";
        if ("3DES".equals(sysParam.getParamValue())) {
            tmkChkVal = Objects.requireNonNull(ByteUtil.bytesToHexString(DES3Util.genCheckValue(ByteUtil.hexStringToByte(tmkKey)))).substring(0, 8);
        } else if ("SM4".equals(sysParam.getParamValue())) {
            tmkChkVal = Objects.requireNonNull(ByteUtil.bytesToHexString(SM4Util.genCheckValue(ByteUtil.hexStringToByte(tmkKey)))).substring(0, 16);
        }

        /*Step 2 ：查询终端密钥信息表，获取终端主密钥*/
        /*获取终端密钥表中的密钥信息*/
        TermKeyMapper termKeyMapper = new TermKeyMapperImpl();
        TermKey tmkTermkey = termKeyMapper.getTermKey(instId, mchntNo, termNo, "00");

        if (null == tmkTermkey
                || StringUtil.isEmptyOrNull(tmkTermkey.getKeyValue())
                || "N".equals(tmkTermkey.getKeyStatus())) {
            if (null == tmkTermkey) {
                /*设置终端主密钥信息*/
                tmkTermkey = new TermKey();
                tmkTermkey.setKeyType("00");
                tmkTermkey.setCreateTime(DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss.SSS"));
                tmkTermkey.setKeyStatus("Y");
                tmkTermkey.setKeyIndex("P" + mchntTermInfo.getMchntNo() + mchntTermInfo.getTermNo());
                /*保存终端主秘钥和校验值*/
                tmkTermkey.setKeyValue(tmkKey + "|" + tmkChkVal);
                tmkTermkey.setKeyDesc("主密钥");
                tmkTermkey.setInstId(instId);
                tmkTermkey.setMchntNo(mchntNo);
                tmkTermkey.setTermNo(termNo);
                tmkTermkey.setAlgorithmType(sysParam.getParamValue());
                /*插入到终端密钥信息表*/
                iRetCode = termKeyMapper.insertTermKey(tmkTermkey);
                /* 判断是否成功插入表中*/
                if (iRetCode != 0) {
                    rglog.error("插入终端主密钥失败，retCode<{}>，tmkTermkey<{}>", iRetCode, JSON.toJSONString(tmkTermkey));
                    throw new BizException(RespCodeEnum.UPDATE_TERM_KEY_ERROR.getRespCode(), RespCodeEnum.UPDATE_TERM_KEY_ERROR.getRespDesc());
                }
            } else {
                tmkTermkey.setKeyValue(tmkKey + "|" + tmkChkVal);
                tmkTermkey.setKeyStatus("Y");
                tmkTermkey.setUpdateTime(DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss.SSS"));
                iRetCode = termKeyMapper.updateTermKeyByPk(tmkTermkey);
                if (iRetCode != 0) {
                    rglog.error("更新终端主密钥失败，retCode<{}>，tmkTermkey<{}>", iRetCode, JSON.toJSONString(tmkTermkey));
                    throw new BizException(RespCodeEnum.UPDATE_TERM_KEY_ERROR.getRespCode(), RespCodeEnum.UPDATE_TERM_KEY_ERROR.getRespDesc());
                }
            }
        } else {
            rglog.debug("终端主秘钥已下载");
            bizResponse.setRspSysId(AppParamConstant.IGAS);
            bizResponse.setRespCode(RespCodeEnum.TERM_TMK_ALRADY_DOWNLOAD.getRespCode());
            bizResponse.setRespDesc(RespCodeEnum.TERM_TMK_ALRADY_DOWNLOAD.getRespDesc());
            bizResponse.setResult(posDownloadTMKResponse);
            return bizResponse;
        }
        /* Step3 ：重组响应结果 */
        posDownloadTMKResponse.setTermMainKey(tmkValue);
        posDownloadTMKResponse.setCheckValue(tmkChkVal);
        posDownloadTMKResponse.setAlgorithmType(sysParam.getParamValue());
        bizResponse.setRspSysId(AppParamConstant.IGAS);
        bizResponse.setRespCode(RespCodeEnum.TRANS_SUCCESS.getRespCode());
        bizResponse.setRespDesc(RespCodeEnum.TRANS_SUCCESS.getRespDesc());
        bizResponse.setResult(posDownloadTMKResponse);
        return bizResponse;
    }

    @Override
    protected void bizAfterCust(Object response) throws Exception {
        BizResponse bizResponse = (BizResponse) response;
        PosDownloadTMKResponse posDownloadTMKResponse = (PosDownloadTMKResponse) bizResponse.getResult();
        ConvertUtil.convertInput(posDownloadTMKResponse);
    }

    /**
     * 使用私钥解密数字信封，得到AES秘钥明文
     *
     * @param decryptDta 数字信封
     * @param privateKey RSA私钥
     * @return 秘钥明文
     * @throws Exception 异常
     */
    private String privateKeyDecrypt(byte[] decryptDta, RSAPrivateKey privateKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        cipher.doFinal(decryptDta);
        return new String(cipher.doFinal(decryptDta));
    }
}
